package com.itesm.distribuidos.mexicantrain;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Stack;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class MainGUI extends JFrame implements ActionListener {
	private static final long serialVersionUID = 1L;
	private MexicanTrainClient client;
	private GameBoard theBoard;
	private Stack<Domino> myHand;
	private JButton exit = new JButton("Exit");
	private JButton play = new JButton("Start Game");
	private JButton draw = new JButton("Draw Tile");
	private JButton pass = new JButton("Pass Turn");
	private JLabel turnLabel = new JLabel("Turn: ");
	private JLabel myScore = new JLabel("My Score : ");
	private JPanel buttonPanel, lowerPanel, boardPanel, handPanel, roundPanel, optionPanel, upperPanel, mainPanel;
	private boolean haventDraw = true;
	private int dots = 0;
	private int roundNumber = 12;
	private int myPlayerNumber, turnNumber;

	public MainGUI() {
		this.setTitle("Mexican Train");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setResizable(false);
		this.setLayout(new BorderLayout());
		this.setIconImage(Toolkit.getDefaultToolkit().getImage("Images/trainIcon.png"));

		buttonPanel = new JPanel(new GridLayout(2,1));
		buttonPanel.add(play);
		buttonPanel.add(exit);
		buttonPanel.setBorder(BorderFactory.createEmptyBorder(15,45,15,45));

		roundPanel = new JPanel();
		roundPanel.add(new JLabel("Engine : "));
		roundPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));

		lowerPanel = new JPanel(new GridLayout(1,3));
		lowerPanel.add(roundPanel);
		lowerPanel.add(myScore);
		lowerPanel.add(buttonPanel);
		lowerPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));

		boardPanel = new JPanel(new BorderLayout());
		boardPanel.add(new JLabel("   Game Board"),BorderLayout.CENTER);
		boardPanel.setBorder(BorderFactory.createEmptyBorder(0,5,0,5));

		handPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
		handPanel.add(new JLabel("My Hand"));
		handPanel.setBorder(BorderFactory.createEmptyBorder(5,5,85,5));

		optionPanel = new JPanel(new GridLayout(3,1));
		optionPanel.add(draw);
		optionPanel.add(pass);
		optionPanel.add(turnLabel);
		optionPanel.setBorder(BorderFactory.createEmptyBorder(25,25,25,25));

		upperPanel = new JPanel(new BorderLayout());
		upperPanel.add(handPanel,BorderLayout.CENTER);
		upperPanel.add(optionPanel,BorderLayout.EAST);

		mainPanel = new JPanel(new BorderLayout());
		mainPanel.add(upperPanel,BorderLayout.NORTH);
		mainPanel.add(boardPanel,BorderLayout.CENTER);
		mainPanel.add(lowerPanel,BorderLayout.SOUTH);

		this.add(mainPanel,BorderLayout.CENTER);
        this.setVisible(true);
        this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } });
        this.setBounds(Toolkit.getDefaultToolkit().getScreenSize().width/5,Toolkit.getDefaultToolkit().getScreenSize().height/100,880,720);

        draw.setEnabled(false);
        pass.setEnabled(false);

        play.addActionListener(this);
		exit.addActionListener(this);
		draw.addActionListener(this);
		pass.addActionListener(this);
	}

	public void actionPerformed(ActionEvent ae) {
		if (ae.getSource() == exit) { System.exit(0); }
		if (ae.getSource() == play) {
			new SwingWorker<Void, Void>() {
                public Void doInBackground() {
                	if (roundNumber == 12) { client = new MexicanTrainClient(getMainGUI()); }
        			myHand = client.getHand();
        			paintHand();
        			paintBoard();
        			return null;
                }
                public void done() {
                	play.setText("G A M E  S T A R T E D");
                	myScore.setText("My Score : " + dots);
                	myPlayerNumber = client.getMyTurn();
                	updateTurn(1);
                	getMainGUI().setTitle(getTitle() + " - Player #" + myPlayerNumber);
                	if (roundNumber != 12) { roundPanel.remove(1); }
                	roundPanel.add(new DominoButton("Images/"+roundNumber+"-"+roundNumber+".jpg").getButton());
                	draw.setEnabled(true);
                	pass.setEnabled(true);
                }
	        }.execute();
			play.setEnabled(false);
			play.setText("Waiting for more players...");
		}
		if (ae.getSource() == draw) {
			if (myPlayerNumber == turnNumber) {
				if (haventDraw) {
					haventDraw = false;
					myHand.push(client.drawTile());
					paintHand();
				}
				else { JOptionPane.showMessageDialog(this,"You already draw this turn!","Rules",JOptionPane.INFORMATION_MESSAGE); }
			}
			else { JOptionPane.showMessageDialog(this,"Is not your turn!","Rules",JOptionPane.INFORMATION_MESSAGE); }
		}
		if (ae.getSource() == pass) {
			if (myPlayerNumber == turnNumber) {
				if (!haventDraw || !draw.isEnabled()) {
					boolean canPass = true;
					if (!theBoard.openDouble()) {
						search : for (int i=0; i<myHand.size(); i++) {
							if (myHand.elementAt(i).getSideA() == theBoard.lastTile(myPlayerNumber) ||
								myHand.elementAt(i).getSideB() == theBoard.lastTile(myPlayerNumber)) {
								canPass = false;
								JOptionPane.showMessageDialog(this,"You can play in your line","Rules",JOptionPane.INFORMATION_MESSAGE);
								break search;
							}
							Stack<Integer> players = theBoard.getPlayersAvailable();
							while (!players.isEmpty()) {
								int player = players.pop();
								if (myHand.elementAt(i).getSideA() == theBoard.lastTile(player) ||
									myHand.elementAt(i).getSideB() == theBoard.lastTile(player)) {
									canPass = false;
									JOptionPane.showMessageDialog(this,"You can play in another line","Rules",JOptionPane.INFORMATION_MESSAGE);
									break search;
								}
							}
						}
					}
					else {
						client.sendToServer(6);
						for (int i=0; i<myHand.size(); i++) {
							if (myHand.elementAt(i).getSideA() == theBoard.lastTile(theBoard.playerWithDouble()) ||
								myHand.elementAt(i).getSideB() == theBoard.lastTile(theBoard.playerWithDouble())) {
								canPass = false;
								JOptionPane.showMessageDialog(this,"You must cover the double","Rules",JOptionPane.INFORMATION_MESSAGE);
								break;
							}
						}
					}
					if (canPass) { client.sendToServer(4); }
				}
				else { JOptionPane.showMessageDialog(this,"You need to draw before pass","Rules",JOptionPane.INFORMATION_MESSAGE); }
			}
			else { JOptionPane.showMessageDialog(this,"Is not your turn!","Rules",JOptionPane.INFORMATION_MESSAGE); }
		}
	}

	public void paintHand() {
		handPanel.removeAll();
		for (int i=0; i<myHand.size(); i++) { handPanel.add(makeButton(i)); }
		handPanel.revalidate();
		this.repaint();
	}

	public void playATile(String tile) {
		if (myPlayerNumber == turnNumber) {
			String[] numbers = tile.substring(tile.indexOf("Images/")+7,tile.indexOf(".jpg")).split("-");
			search : for (int i=0; i<myHand.size(); i++) {
				if (myHand.elementAt(i).getSideA() == Integer.parseInt(numbers[0]) && 
					myHand.elementAt(i).getSideB() == Integer.parseInt(numbers[1])) {
					if (theBoard.openDouble() && theBoard.getLock()) {
						if (theBoard.lastTile(theBoard.playerWithDouble()) == myHand.elementAt(i).getSideA()) {
							if (theBoard.playerWithDouble() == myPlayerNumber) { client.sendToServer(5); }
							Domino temp = myHand.elementAt(i);
							myHand.remove(myHand.elementAt(i));
							if (myHand.size() == 0) { temp.setFinal(true); }
							client.setTileOnBoard(temp,theBoard.playerWithDouble());
							paintHand();
							break search;
						}
						else { JOptionPane.showMessageDialog(this,"You must cover the double","Rules",JOptionPane.INFORMATION_MESSAGE); }
					}
					else {
						if (theBoard.openDouble()) { client.sendToServer(6); }
						if (theBoard.lastTile(myPlayerNumber) == myHand.elementAt(i).getSideA()) {
							if (theBoard.isPlayerOpen(myPlayerNumber)) { client.sendToServer(5); }
							Domino temp = myHand.elementAt(i);
							myHand.remove(myHand.elementAt(i));
							if (myHand.size() == 0) { temp.setFinal(true); }
							client.setTileOnBoard(temp,myPlayerNumber);
							paintHand();
							break search;
						}
						Stack<Integer> players = theBoard.getPlayersAvailable();
						while (!players.isEmpty()) {
							int player = players.pop();
							if (theBoard.lastTile(player) == myHand.elementAt(i).getSideA()) {
								Domino temp = myHand.elementAt(i);
								myHand.remove(myHand.elementAt(i));
								if (myHand.size() == 0) { temp.setFinal(true); }
								client.setTileOnBoard(temp,player);
								paintHand();
								break search;
							}
						}
					}
				}
			}
		}
		else { JOptionPane.showMessageDialog(this,"Is not your turn!","Rules",JOptionPane.INFORMATION_MESSAGE); }
	}

	public void flipTile(String tile) {
		String[] numbers = tile.substring(tile.indexOf("Images/")+7,tile.indexOf(".jpg")).split("-");
		for (int i=0; i<myHand.size(); i++) {
			if (myHand.elementAt(i).getSideA() == Integer.parseInt(numbers[0]) && 
				myHand.elementAt(i).getSideB() == Integer.parseInt(numbers[1])) {
				myHand.elementAt(i).setSideA(Integer.parseInt(numbers[1]));
				myHand.elementAt(i).setSideB(Integer.parseInt(numbers[0]));
				paintHand();
				break;
			}
		}
	}

	public JButton makeButton(int i) {
		JButton temp = new DominoButton("Images/"+myHand.elementAt(i).getSideA()+"-"+myHand.elementAt(i).getSideB()+".jpg").getButton();
		temp.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
            	playATile(ae.getSource().toString());
			}
		});
		temp.addMouseListener(new MouseAdapter() {
            public void mouseReleased(MouseEvent me) {
                if (SwingUtilities.isRightMouseButton(me)) {
                	flipTile(me.getSource().toString());
                }
            }                  
        });
		return temp;
	}

	public void paintBoard() {
		theBoard = client.getGameBoard();
		boardPanel.removeAll();
		boardPanel.add(theBoard);
		boardPanel.revalidate();
		this.repaint();
	}

	public void updateTurn(int turn) {
		if (turn == 100 || turn == 200) { finishGame(turn); }
		else {
			turnNumber = turn;
			if (myPlayerNumber == turnNumber) {
				haventDraw = true;
				turnLabel.setText("Its your turn!");
			}
			else { turnLabel.setText("Is player #" + turnNumber + " turn"); }
			paintBoard();
		}
	}

	public void updateBoneyard(int remaining) {
		draw.setText("Draw Tile (" + remaining + ")");
		if (remaining == 0) { draw.setEnabled(false); }
	}

	public MainGUI getMainGUI() {
		return this;
	}

	public void finishGame(int type) {
		if (type == 100) {
			if (myPlayerNumber == turnNumber) { JOptionPane.showMessageDialog(this,"You WON!","Rules",JOptionPane.INFORMATION_MESSAGE); }
			else { JOptionPane.showMessageDialog(this,"Player #"+turnNumber+" WON, total points: "+remainingDots(),"Rules",JOptionPane.INFORMATION_MESSAGE); }
		}
		else { JOptionPane.showMessageDialog(this,"Game stalled, total points: "+remainingDots(),"Rules",JOptionPane.INFORMATION_MESSAGE); }
		nextRound();
	}

	public void nextRound() {
		roundNumber--;
		play.setText("Play Next Round");
		play.setEnabled(true);
	}

	public int remainingDots() {
		while(!myHand.isEmpty()) {
			Domino temp = myHand.pop();
			dots += temp.getSideA() + temp.getSideB();
		}
		return dots;
	}

	public static void main(String args[]) {
		Splasher splash = new Splasher();
		try { splash.join(); }
		catch (InterruptedException ie) { }
		SwingUtilities.invokeLater(new Runnable() {
			public void run() { new MainGUI(); }
		});
	}
}


class SplashScreen extends JFrame {
	private static final long serialVersionUID = 3L;
	public SplashScreen() {
		this.setUndecorated(true);
		this.add(new JLabel(new ImageIcon("images/splash.jpg")));
		this.setBounds(Toolkit.getDefaultToolkit().getScreenSize().height/2,Toolkit.getDefaultToolkit().getScreenSize().width/6,200,200);
		this.pack();
        this.setVisible(true);
	}
}


class Splasher extends Thread {
	public Splasher() {
		SplashScreen startup = new SplashScreen();
		try { Splasher.sleep(3000); }
		catch (InterruptedException e) { }
		startup.dispose();
	}
}